package com.study.project.config.redis;

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import javax.annotation.Resource;

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Component;

import com.study.project.config.exception.BizErrorCode;
import com.study.project.config.exception.BusinessException;

import lombok.extern.slf4j.Slf4j;

/**
 * @author yangjie
 * 
 * @see RedisLockUtil
 * @date 2024/4/13-17:12
 */
@Slf4j
@Component
public class RedisLockUtil {

    @Resource
    private Redisson redisson;

    public <T> T locksupplier(String lockKey, Supplier<T> supplier, BizErrorCode bizErrorCode) {
        // 1.获取锁，只要锁的名字一样，获取到的锁就是同一把锁。
        RLock lock = redisson.getLock(lockKey);
        try {
            // 2.加锁
            boolean success = lock.tryLock(1, 1, TimeUnit.SECONDS);
            if (!success) {
                throw new BusinessException(BizErrorCode.MARKETING_ACTIVITY_JOIN_FREQUENT);
            }
            return supplier.get();
        } catch (Exception e) {
            if (Objects.nonNull(bizErrorCode)) {
                throw new BusinessException(bizErrorCode);
            }
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("lock released");
            }
        }
        return null;
    }

    /**
     * redis分布式锁-加锁
     *
     * @param lockKey 分布式锁key
     * @param waitTime 最长等待时间
     * @param leaseTime 锁自动释放的时间
     * @param unit 单位
     * @return
     */
    public boolean lock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        try {
            RLock lock = redisson.getLock(lockKey);
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }

    /**
     * redis分布式锁-解锁
     *
     * @param lockKey 分布式锁key
     * @return
     */
    public void unLock(String lockKey) {
        RLock lock = redisson.getLock(lockKey);
        if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
            log.info("lock released");
        }
    }
}
